package com.bwjf.dzfp.common.util;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.BasicHttpEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.pool.PoolStats;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;

/**
 * 通过http连接池来管理连接
 * 注：closeConnections()方法需要另起一个线程，每隔一段时间执行一次回收已成功或者已过期的连接。（如用spring的定时任务）
 * 
 * @author liluqing
 * @date 2016-06-01
 */
public class HttpUtils {
	static Logger logger = Logger.getLogger(HttpUtils.class);

	private static PoolingHttpClientConnectionManager cm = null;

	// 定义编码格式 UTF-8
	public static final String URL_PARAM_DECODECHARSET_UTF8 = "UTF-8";
	// 定义编码格式 GBK
	public static final String URL_PARAM_DECODECHARSET_GBK = "GBK";
	private static final String URL_PARAM_CONNECT_FLAG = "&";
	private static final String EMPTY = "";
	private static int connectionTimeOut = 25000;
	private static int socketTimeOut = 25000;
	// 链接管理器容量，将每个路由的最大连接数增加到200
	private static int maxTotal = 100;
	// 将每个路由的默认最大并发数。超过最大并发数的线程将进入阻塞状态。直到有链接过期或被关闭。
	private static int maxPerRoute = 20;

	static {
		// 创建链接管理器
		cm = new PoolingHttpClientConnectionManager();
		// 链接管理器容量，将每个路由的最大连接数增加到200
		cm.setMaxTotal(maxTotal);
		// 将每个路由的默认最大并发数。超过最大并发数的线程将进入阻塞状态。直到有链接过期或被关闭。
		cm.setDefaultMaxPerRoute(maxPerRoute);
		// 定义将某个路由的最大并发数
		// HttpHost localhost = new HttpHost("locahost", 80);
		// cm.setMaxPerRoute(new HttpRoute(localhost), 50);//设置对于某个目标主机的最大并发数
	}

	/**
	 * 重连接管理器中获取CloseableHttpClient对象
	 * 
	 * @return
	 */
	public static CloseableHttpClient getCloseableHttpClient() {
		return HttpClients.custom().setConnectionManager(cm).build();
	}

	/**
	 * 关闭回收所有已经请求成功，或者请求超时的连接 该方法需用spring做一个定时任务每隔5秒（根据具体情况来配置）执行一次
	 */
	public static void closeConnections() {
		cm.closeExpiredConnections();
		cm.closeIdleConnections(30, TimeUnit.SECONDS);
	}

	/**
	 * 获取连接管理器状态。
	 */
	public static String getState() {
		PoolStats state = cm.getTotalStats();
		return "http连接池信息：[可用连接:" + state.getAvailable() + "; 正在执行的连接:"
				+ state.getLeased() + "; 被阻塞的连接:" + state.getPending()
				+ "; 最大可用连接数:" + state.getMax() + "]";
	}

	/**
	 * 以UTF8的方式发送报文。当目标服务器采用UTF8时，需用该方法。否则会导致请求参数乱码，目标服务器无法解析
	 * 
	 * @param url
	 *            连接地址
	 * @param xml
	 *            报文
	 * @param decode
	 *            响应报文的编码
	 * @return
	 */
	public static String httpPostForXMLUTF8(String url, String xml,
			String decode) {
		String result = null;
		CloseableHttpClient client = getCloseableHttpClient();// 创建会话
		HttpPost httppost = new HttpPost(url);
		httppost.setHeader("Content-Type", "text/xml;charset=UTF-8");// 设置请求头
		StringEntity entity = new StringEntity(xml, ContentType.create(
				"text/xml", Consts.UTF_8));// 定义请求消息实体
		entity.setChunked(true);
		RequestConfig requestConfig = RequestConfig.custom()
				.setSocketTimeout(socketTimeOut)
				.setConnectTimeout(connectionTimeOut).build();// 设置连接超时和socket超时时间
		httppost.setConfig(requestConfig);
		httppost.setEntity(entity);
		CloseableHttpResponse response = null;
		try {
			response = client.execute(httppost);// 执行请求
			HttpEntity ety = response.getEntity();// 获取响应实体
			result = EntityUtils.toString(ety, decode);// 由于税控服务器的编码格式为GBK，故需将返回内容按GBK的方式来解码
		} catch (ClientProtocolException e) {
			logger.error(
					"request url=" + url + ", exception, msg=" + e.getMessage(),
					e);
		} catch (IOException e) {
			logger.error(
					"request url=" + url + ", exception, msg=" + e.getMessage(),
					e);
		} finally {
			try {
				if (response != null) {
					response.close();// 关闭响应流
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return result;
	}

	/**
	 * 以GBK的方式发送报文。当目标服务器采用GBK时，需用该方法。否则会导致请求参数乱码，目标服务器无法解析
	 * 
	 * @param url
	 *            连接地址
	 * @param xml
	 *            报文
	 * @param decode
	 *            响应报文的编码
	 * @return
	 */
	public static String httpPostForXMLGBK(String url, String xml, String decode) {
		String result = null;
		CloseableHttpClient client = getCloseableHttpClient();// 创建会话
		HttpPost httppost = new HttpPost(url);
		httppost.setHeader("Content-Type", "text/xml;charset=UTF-8");// 设置请求头
		StringEntity entity = new StringEntity(xml, "gbk");// 定义请求消息实体
		entity.setChunked(true);
		RequestConfig requestConfig = RequestConfig.custom()
				.setSocketTimeout(socketTimeOut)
				.setConnectTimeout(connectionTimeOut).build();// 设置连接超时和socket超时时间
		httppost.setConfig(requestConfig);
		httppost.setEntity(entity);
		CloseableHttpResponse response = null;
		try {
			response = client.execute(httppost);// 执行请求
			HttpEntity ety = response.getEntity();// 获取响应实体
			result = EntityUtils.toString(ety, decode);// 由于税控服务器的编码格式为GBK，故需将返回内容按GBK的方式来解码
		} catch (ClientProtocolException e) {
			logger.error(
					"request url=" + url + ", exception, msg=" + e.getMessage(),
					e);
		} catch (IOException e) {
			logger.error(
					"request url=" + url + ", exception, msg=" + e.getMessage(),
					e);
		} finally {
			try {
				if (response != null) {
					response.close();// 关闭响应流
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return result;
	}

	/**
	 * post请求模拟form表单提交
	 * 
	 * @param url
	 *            连接地址
	 * @param params
	 *            post请求参数
	 * @param enc
	 *            请求参数编码
	 * @return
	 */
	public static String httpPost(String url, Map<String, String> params,
			String enc) {
		String result = null;
		CloseableHttpClient client = getCloseableHttpClient();// 创建会话
		HttpPost httppost = new HttpPost(url);
		// 设置头类型为from表单提交
		httppost.setHeader("Content-Type",
				"application/x-www-form-urlencoded;charset=" + enc);
		RequestConfig requestConfig = RequestConfig.custom()
				.setSocketTimeout(socketTimeOut)
				.setConnectTimeout(connectionTimeOut).build();// 设置连接超时和socket超时时间
		httppost.setConfig(requestConfig);

		UrlEncodedFormEntity entity = getUrlEncodedFormEntity(params);
		httppost.setEntity(entity);

		CloseableHttpResponse response = null;
		try {
			response = client.execute(httppost);// 执行请求
			HttpEntity ety = response.getEntity();// 获取响应实体
			result = EntityUtils.toString(ety, URL_PARAM_DECODECHARSET_UTF8);
		} catch (ClientProtocolException e) {
			logger.error(
					"request url=" + url + ", exception, msg=" + e.getMessage(),
					e);
		} catch (IOException e) {
			logger.error(
					"request url=" + url + ", exception, msg=" + e.getMessage(),
					e);
		} finally {
			try {
				if (response != null) {
					response.close();// 关闭响应流
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return result;
	}

	/**
	 * get请求
	 * 
	 * @param url
	 * @return
	 */
	public static String httpGet(String url) {
		HttpGet httpGet = new HttpGet(url);
		CloseableHttpResponse response = null;
		CloseableHttpClient httpClient = getCloseableHttpClient();
		RequestConfig requestConfig = RequestConfig.custom()
				.setSocketTimeout(socketTimeOut)
				.setConnectTimeout(connectionTimeOut).build();
		httpGet.setConfig(requestConfig);
		try {
			response = httpClient.execute(httpGet, new BasicHttpContext());
			if (response.getStatusLine().getStatusCode() != 200) {
				return null;
			}
			HttpEntity entity = response.getEntity();
			if (entity != null) {
				String resultStr = EntityUtils.toString(entity,
						URL_PARAM_DECODECHARSET_UTF8);
				return resultStr;
			}
		} catch (IOException e) {
			logger.error(
					"request url=" + url + ", exception, msg=" + e.getMessage(),
					e);
		} finally {
			if (response != null)
				try {
					response.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
		return null;
	}

	/**
	 * get请求模拟表单提交
	 * 
	 * @param url
	 * @param params
	 * @param enc
	 * @return
	 */
	public static String httpGet(String url, Map<String, String> params,
			String enc) {
		StringBuilder strtTotalURL = new StringBuilder(EMPTY);
		if (strtTotalURL.indexOf("?") == -1) {
			strtTotalURL.append(url).append("?").append(getUrl(params, enc));
		} else {
			strtTotalURL.append(url).append("&").append(getUrl(params, enc));
		}
		HttpGet httpGet = new HttpGet(strtTotalURL.toString());
		// 设置头类型为from表单提交
		httpGet.setHeader("Content-Type",
				"application/x-www-form-urlencoded;charset=" + enc);
		CloseableHttpResponse response = null;
		CloseableHttpClient httpClient = getCloseableHttpClient();
		RequestConfig requestConfig = RequestConfig.custom()
				.setSocketTimeout(socketTimeOut)
				.setConnectTimeout(connectionTimeOut).build();
		httpGet.setConfig(requestConfig);
		try {
			response = httpClient.execute(httpGet, new BasicHttpContext());
			if (response.getStatusLine().getStatusCode() != 200) {
				return null;
			}
			HttpEntity entity = response.getEntity();
			if (entity != null) {
				String resultStr = EntityUtils.toString(entity,
						URL_PARAM_DECODECHARSET_UTF8);
				return resultStr;
			}
		} catch (IOException e) {
			logger.error("request url=" + strtTotalURL.toString()
					+ ", exception, msg=" + e.getMessage(), e);
		} finally {
			if (response != null)
				try {
					response.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
		return null;
	}

	/**
	 * 据Map生成URL字符串
	 * 
	 * @param map
	 *            Map
	 * @param valueEnc
	 *            URL编码
	 * @return URL
	 */
	private static String getUrl(Map<String, String> map, String valueEnc) {

		if (null == map || map.keySet().size() == 0) {
			return (EMPTY);
		}
		StringBuffer url = new StringBuffer();
		Set<String> keys = map.keySet();
		for (Iterator<String> it = keys.iterator(); it.hasNext();) {
			String key = it.next();
			if (map.containsKey(key)) {
				String val = map.get(key);
				String str = val != null ? val : EMPTY;
				try {
					str = URLEncoder.encode(str, valueEnc);
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				}
				url.append(key).append("=").append(str)
						.append(URL_PARAM_CONNECT_FLAG);
			}
		}
		String strURL = EMPTY;
		strURL = url.toString();
		if (URL_PARAM_CONNECT_FLAG.equals(EMPTY
				+ strURL.charAt(strURL.length() - 1))) {
			strURL = strURL.substring(0, strURL.length() - 1);
		}
		return (strURL);
	}

	public static UrlEncodedFormEntity getUrlEncodedFormEntity(
			Map<String, String> params) {
		List<NameValuePair> formparams = new ArrayList<NameValuePair>();
		if (params != null && params.size() > 0) {
			Iterator<Entry<String, String>> it = params.entrySet().iterator();
			while (it.hasNext()) {
				Entry<String, String> en = it.next();
				formparams.add(new BasicNameValuePair(en.getKey(), en
						.getValue()));
			}
		}
		UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams,
				Consts.UTF_8);
		entity.setChunked(true);
		return entity;
	}

	public static void main(String[] args) {
		// String
		// xml="<?xml version=\"1.0\" encoding=\"gbk\"?><business id=\"DJXXCX\" comment=\"登记信息查询\"><REQUEST_COMMON_DJXXCX class=\"REQUEST_COMMON_DJXXCX\"></REQUEST_COMMON_DJXXCX></business>";
		// String url="http://60.190.46.106:9998/SKServer/SKDo";
		// 连接地址
		String url = "http://www.366tax.cn:8090/dzfpkp/skfw.htm";
		String xml = "<?xml version=\"1.0\" encoding=\"gbk\"?><interface><globalInfo><appId>DZFPQZ</appId><interfaceId/><interfaceCode>FPKJ</interfaceCode><requestCode>test</requestCode><requestSecret>4cdbc040657a4847b2667e31d9e2c3d9</requestSecret><requestTime>2016-05-22 11:49:06</requestTime><responseCode>DZFPQZ</responseCode><dataExchangeId></dataExchangeId></globalInfo><Data><dataDescription><zipCode>0</zipCode></dataDescription><content>PFJFUVVFU1RfQ09NTU9OX0ZQS0ogY2xhc3M9IlJFUVVFU1RfQ09NTU9OX0ZQS0oiPjxGUFFRTFNIPjwvRlBRUUxTSD48S1BMWD4wPC9LUExYPjxYU0ZfTlNSU0JIPjkxMzMwMjI3MTQ0NDAwMDA1RTwvWFNGX05TUlNCSD48WFNGX01DPuWugeazoumTtuihjDwvWFNGX01DPjxYU0ZfRFpESD7mtZnmsZ/nnIHlroHms6LluILplYfmtbfljLrpqobpqbzooZfpgZPmsYfplKbot68xOOWPtyAwNTc0LTgzODg3Njg1PC9YU0ZfRFpESD48WFNGX1lIWkg+5bu66KGM5a6B5rOi5biC5YiG6KGM5Zu95a626auY5paw5Yy65pSv6KGMIDMzMTAxOTg1MTM2MDUwNTEyODQyPC9YU0ZfWUhaSD48R01GX05TUlNCSD44ODg4ODg4ODg4ODg4ODg8L0dNRl9OU1JTQkg+PEdNRl9NQz7mtYvor5XnlKjmiLcwMDE8L0dNRl9NQz48R01GX0RaREg+MTExMTEgMTM5NjcwNjc2NTY8L0dNRl9EWkRIPjxHTUZfWUhaSD4wMDIwMDMgMDAyMDAzPC9HTUZfWUhaSD48R01GX1NKSD48L0dNRl9TSkg+PEdNRl9EWllYPjwvR01GX0RaWVg+PEtQUj7lkLTpl67msZ88L0tQUj48U0tSPuWQtOmXruaxnzwvU0tSPjxGSFI+5ZC06Zeu5rGfPC9GSFI+PFlGUF9ETT48L1lGUF9ETT48WUZQX0hNPjwvWUZQX0hNPjxKU0hKPjMzMC4wMDwvSlNISj48SEpKRT4zMTEuMzI8L0hKSkU+PEhKU0U+MTguNjg8L0hKU0U+PEJaPjwvQlo+PENPTU1PTl9GUEtKX1hNWFhTIGNsYXNzPSJDT01NT05fRlBLSl9YTVhYIiBzaXplPSIxIj48Q09NTU9OX0ZQS0pfWE1YWD48RlBIWFo+MDwvRlBIWFo+PFhNTUM+5oqA5pyv57u05oqk6LS5KDIwMTYuMDQuMjgtMjAxNy4wNC4yNyk8L1hNTUM+PEdHWEg+PC9HR1hIPjxEVz7lubQ8L0RXPjxYTVNMPjE8L1hNU0w+PFhNREo+MzExLjMyMDc1NTwvWE1ESj48WE1KRT4zMTEuMzI8L1hNSkU+PFNMPjAuMDY8L1NMPjxTRT4xOC42ODwvU0U+PC9DT01NT05fRlBLSl9YTVhYPjwvQ09NTU9OX0ZQS0pfWE1YWFM+PC9SRVFVRVNUX0NPTU1PTl9GUEtKPg==</content></Data></interface>";
		String result = httpPostForXMLUTF8(url, xml, "utf-8");
		System.out.println(result);
	}
}
